/*!
 * \file amqp_detect.c
 *
 * \brief amqp 协议判断
 *
 * All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.\n
 *
 * \version 2.0
 * \author geyh
 * \date 2015/12/24
 *
 */
#include <stdio.h>

#include "conntrack.h"
#include "module.h"
#include "frct_comm.h"
#include "frservice_comm.h"
#include "amqp_detect.h"
#include "config.h"
#include "amqp_basedata_type.h"

extern void *g_p_apm_context;

/*******************************************************************************
*  FUNC     :  判断是否是amqp
*  ARGS     :  p_st_conn                    输入数据信息
*           :  p_st_packet                 输入数据
*  RTN      :  APM_L7_IS_NO                 不是amqp数据
*           :  APM_L7_IS_YES                是amqp数据
*  NOTE     :
*******************************************************************************/
int amqp_detect_1_0_0(Conntrack *p_st_conn, Apm_packet *p_st_packet)
{
    char *p_data = NULL;
    int i_data_len = 0;
    int i_ret = 0, i_type = 0;

    if(p_st_conn == NULL || p_st_packet == NULL || p_st_packet->p_payload == NULL || p_st_packet->ui_payload_len < 8)
        return APM_L7_IS_NO;

    p_data = p_st_packet->p_payload;

    if(strncmp(p_data, "AMQP", 4) == 0)
    {
        i_type = 1;
        goto END;
    }

    i_data_len = pntohi(p_st_packet->p_payload);
    if((unsigned int)i_data_len != p_st_packet->ui_payload_len)
        return APM_L7_IS_NO;

    p_data += 4;
    i_data_len -= 4;

    //doff
    if(*p_data != '\x02' || !i_data_len)
        return APM_L7_IS_NO;

    p_data++;
    i_data_len--;

    //type
    if(*p_data != '\x00' || !i_data_len)
        return APM_L7_IS_NO;

    p_data++;
    i_data_len--;

    END:
    i_ret = frct_get_direct_known(p_st_conn);
    if(i_ret == FR_DIRECT_KNOWN)
    {
        if(p_st_packet->i_direction == FR_DIRECT_CLIENT2SRV)
            frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, NULL, 0);
        else
            frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, NULL, 0);

        return APM_L7_IS_YES;
    }

    //方向判断
    if(i_type)
    {
        if(p_st_packet->ui_payload_len > 12)
        {
            i_data_len = pntohi(p_st_packet->p_payload + 8);
            if((unsigned int)i_data_len != p_st_packet->ui_payload_len + 8)
                return APM_L7_IS_NO;
            else
            {
                //response
                frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
                frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, NULL, 0);
                return APM_L7_IS_YES;
            }
        }
        else if(p_st_packet->ui_payload_len == 8)
        {
            //request
            frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);
            frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, NULL, 0);
            return APM_L7_IS_YES;
        }

        i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, NULL, 0, NULL);
        if (i_ret == FR_SUCC)
        {
            if (frct_get_direct_known(p_st_conn) == FR_DIRECT_UNKNOWN)
                frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);

            return APM_L7_IS_YES;
        }

        i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, NULL, 0, NULL);
        if (i_ret == FR_SUCC)
        {
            if (frct_get_direct_known(p_st_conn) == FR_DIRECT_UNKNOWN)
                frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
            return APM_L7_IS_YES;
        }

        return APM_L7_IS_NO;
    }

    //跳过channel
    p_data += 2;
    i_data_len -= 2;

    //跳过00
    p_data ++;
    i_data_len--;

    // 53
    if(i_data_len <= 0 || *p_data != '\x53')
        return FR_FAIL;
    p_data ++;
    i_data_len--;

    //disposition
    if(i_data_len <= 0 || *p_data== '\x15')
    {
        frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
        frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, NULL, 0);
        return APM_L7_IS_YES;
    }

    i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, NULL, 0, NULL);
    if (i_ret == FR_SUCC)
    {
        if (frct_get_direct_known(p_st_conn) == FR_DIRECT_UNKNOWN)
            frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);

        return APM_L7_IS_YES;
    }

    i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, NULL, 0, NULL);
    if (i_ret == FR_SUCC)
    {
        if (frct_get_direct_known(p_st_conn) == FR_DIRECT_UNKNOWN)
            frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
        return APM_L7_IS_YES;
    }

    return APM_L7_IS_NO;

}


int amqp_direction_detect_0_9_0(unsigned short us_class, unsigned short us_method, Conntrack *p_st_conn, Apm_packet *p_st_packet)
{
    int i_ret = 0;

    if(p_st_conn == NULL || p_st_packet == NULL)
        return FR_FAIL;

    switch (us_class)
    {
        //connect
        case 10:
            switch (us_method)
            {
                //Start
                case 10:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
                    break;

                //Start-Ok
                case 11:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);
                    break;

                //tune
                case 30:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
                    break;

                //tune ok
                case 31:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);
                    break;

                //open
                case 40:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);
                    break;

                //open ok
                case 41:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
                    break;

                default:
                    return FR_FAIL;
            }
            break;

        //channel
        case 20:
            switch (us_method)
            {
                //open
                case 10:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);
                    break;

                //open ok
                case 11:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
                    break;

                default:
                    return FR_FAIL;
            }
            break;

            //exchange
        case 40:
            switch (us_method)
            {
                //Declare
                case 10:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);
                    break;

                //Declare-Ok
                case 11:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
                    break;

                default:
                    return FR_FAIL;
            }

            break;

            //queue
        case 50:
            switch (us_method)
            {
                //Declare
                case 10:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);
                    break;

                    //Declare-Ok
                case 11:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
                    break;

                    //bind
                case 20:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);
                    break;

                    //bind ok
                case 21:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
                    break;

                default:
                    return FR_FAIL;
            }
            break;

            //basic
        case 60:
            switch (us_method)
            {
                //Consume
                case 20:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);
                    break;

                //Consume-Ok
                case 21:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
                    break;

                //Publish
                case 40:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);
                    break;

                //deliver
                case 60:
                    i_ret = frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
                    break;

                    //其他
                default:
                    return FR_FAIL;
            }
            break;

            //其他不解析
        default:
            return FR_FAIL;
    }

    return i_ret;
}

/*******************************************************************************
*  FUNC     :  判断是否是amqp
*  ARGS     :  p_st_conn                    输入数据信息
*           :  p_st_packet                 输入数据
*  RTN      :  APM_L7_IS_NO                 不是amqp数据
*           :  APM_L7_IS_YES                是amqp数据
*  NOTE     :
*******************************************************************************/
int amqp_detect_0_9_0(Conntrack *p_st_conn, Apm_packet *p_st_packet)
{
    char *p_data = NULL;
    unsigned int ui_tmp = 0;
    int i_ret = 0, i_type = 0, i_data_len = 0;
    unsigned short us_class = 0, us_method = 0;

    if(p_st_conn == NULL || p_st_packet == NULL || p_st_packet->p_payload == NULL || p_st_packet->ui_payload_len < 8)
        return APM_L7_IS_NO;

    p_data = p_st_packet->p_payload;
    i_data_len = p_st_packet->ui_payload_len;

    if(strncmp(p_data, "AMQP", 4) == 0)
    {
        i_type = 1;
        goto END;
    }

    //keep alive
    if(*p_data == '\x08')
    {
        if(memcmp (p_data, "\x08\x00\x00\x00\x00\x00\x00\xce", 8) == 0)
        {
            i_ret = frct_get_direct_known(p_st_conn);
            if(i_ret == FR_DIRECT_KNOWN)
            {
                if(p_st_packet->i_direction == FR_DIRECT_CLIENT2SRV)
                    frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, NULL, 0);
                else
                    frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, NULL, 0);

                return APM_L7_IS_YES;
            }
        }

        return APM_L7_IS_NO;
    }


    //method 1:3
    if(*p_data == '\x00' || *p_data > '\x03')
        return APM_L7_IS_NO;

    //跳过method chanel
    i_data_len -= 3;
    p_data += 3;

    //frame 长度
    ui_tmp = pntohi (p_data);
    p_data += 4;
    i_data_len -= 4;

    if((unsigned int)i_data_len <= ui_tmp)
        return APM_L7_IS_NO;

    if(p_data[ui_tmp] != '\xce')
        return APM_L7_IS_NO;

END:
    i_ret = frct_get_direct_known(p_st_conn);
    if(i_ret == FR_DIRECT_KNOWN)
    {
        if(p_st_packet->i_direction == FR_DIRECT_CLIENT2SRV)
            frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, NULL, 0);
        else
            frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, NULL, 0);

        return APM_L7_IS_YES;
    }

    //方向判断
    if(i_type)
    {
        if(p_st_packet->ui_payload_len > 12)
        {
            i_data_len = pntohi(p_st_packet->p_payload + 8);
            if((unsigned int)i_data_len != p_st_packet->ui_payload_len + 8)
                return APM_L7_IS_NO;
            else
            {
                //response
                frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
                frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, NULL, 0);
                return APM_L7_IS_YES;
            }
        }
        else if(p_st_packet->ui_payload_len == 8)
        {
            //request
            frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);
            frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, NULL, 0);
            return APM_L7_IS_YES;
        }

        i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, NULL, 0, NULL);
        if (i_ret == FR_SUCC)
        {
            if (frct_get_direct_known(p_st_conn) == FR_DIRECT_UNKNOWN)
                frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);

            return APM_L7_IS_YES;
        }

        i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, NULL, 0, NULL);
        if (i_ret == FR_SUCC)
        {
            if (frct_get_direct_known(p_st_conn) == FR_DIRECT_UNKNOWN)
                frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);
            return APM_L7_IS_YES;
        }

        return APM_L7_IS_NO;
    }

    if(p_st_packet->p_payload[0] != '\x01')
        return APM_L7_IS_NO;

    //方向判断
    if(i_data_len < 5)
        return APM_L7_IS_NO;
    us_class = pntohs (p_data);
    p_data += 2;

    us_method = pntohs (p_data);

    //方向设定
    i_ret = amqp_direction_detect_0_9_0(us_class, us_method, p_st_conn, p_st_packet);
    if(i_ret == FR_SUCC)
    {
        if(p_st_packet->i_direction == FR_DIRECT_CLIENT2SRV)
            frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, NULL, 0);
        else
            frservice_add(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, NULL, 0);

        return APM_L7_IS_YES;
    }

    i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->dst_ip, p_st_packet->dst_port, NULL, 0, NULL);
    if (i_ret == FR_SUCC)
    {
        if (frct_get_direct_known(p_st_conn) == FR_DIRECT_UNKNOWN)
            frcap_set_cs_srv(p_st_conn, p_st_packet->dst_ip, p_st_packet->dst_port);

        return APM_L7_IS_YES;
    }

    i_ret = frservice_get(g_p_apm_context, MODULE_L7PROTO, p_st_packet->src_ip, p_st_packet->src_port, NULL, 0, NULL);
    if (i_ret == FR_SUCC)
    {
        if (frct_get_direct_known(p_st_conn) == FR_DIRECT_UNKNOWN)
            frcap_set_cs_srv(p_st_conn, p_st_packet->src_ip, p_st_packet->src_port);

        return APM_L7_IS_YES;
    }

    return APM_L7_IS_NO;

}

